package com.gitee.wsl.common.chart.render.background.axis.step

import com.gitee.wsl.transform.format.Format
import java.time.LocalDate
import java.time.LocalDateTime

import java.time.temporal.ChronoUnit
import java.time.temporal.TemporalUnit
import java.time.Duration

interface StepCalculator<T>:Iterator<T> {

    var format: Format<T>

    fun nextFormat():String {
        return format.invoke(next())
    }

    fun getValue(index:Int):T?

    fun reset()

}

class StringStepCalculatorWarp<T>(val baseStepCalculator:StepCalculator<T>):StepCalculator<String>{
    override var format: Format<String> =Format{it}

    override fun getValue(index: Int): String? {
        val value = baseStepCalculator.getValue(index)?:return null
        return baseStepCalculator.format.invoke(value)
    }

    override fun reset() {
        baseStepCalculator.reset()
    }

    override fun hasNext(): Boolean {
        return baseStepCalculator.hasNext()
    }

    override fun next(): String {
       return baseStepCalculator.nextFormat()
    }

}


open class ListStepCalculator<T>(val valueList:List<T>):StepCalculator<T>{

    override var format: Format<T> = Format{ it.toString() }

    var currentStepIndex = 0

    override fun hasNext(): Boolean {
       return currentStepIndex < valueList.size
    }

    override fun next(): T {
        val next = valueList[currentStepIndex]
        currentStepIndex++
        return next
    }

    override fun nextFormat(): String {
        val next = next()
        return format.invoke(next)
    }

    override fun getValue(index: Int): T? {
        return valueList.getOrNull(index)
    }

    override fun reset() {
        currentStepIndex = 0
    }

    companion object{
        fun <T> default() = ListStepCalculator<T>(emptyList())
    }
}


abstract class AbstractStepCalculator<T>(val start:T, val end:T, val step:Int):StepCalculator<T>{

    override var format: Format<T> = Format{ it.toString() }

    var currentStepIndex = 0
    var lastValue:T = start


    override fun next(): T {
        lastValue = getNext(currentStepIndex,step,lastValue,start, end)
        currentStepIndex++
        return lastValue
    }

    abstract fun getNext(stepIndex:Int,count:Int,currentValue:T,start:T,end:T):T

    abstract fun getCount():Int

    override fun nextFormat(): String {
        val next = next()
        return format.invoke(next)
    }

    override fun getValue(index: Int): T? {
        if(index>=step) return null
        return getNext(index,step, lastValue,start, end)
    }

    override fun reset() {
        currentStepIndex = 0
        lastValue = start
    }
}


class IntStepCalculator(start: Int, end:Int, step:Int) : AbstractStepCalculator<Int>(start,end, step) {

    override fun hasNext(): Boolean {
       return (start + currentStepIndex * step) <= (end + step)
    }

    override fun getNext(stepIndex: Int, count: Int, currentValue: Int, start: Int, end: Int): Int {
        return start + stepIndex * step
    }

    override fun getCount():Int{
        return (end - start) / step + 1
    }

    companion object{
        fun instance(intRange: IntRange, step: Int, format: Format<Int>?=null):IntStepCalculator{
            return IntStepCalculator(intRange.first,intRange.last,step).apply {
                format?.let {
                    this.format=it
                }
            }
        }
    }
}

fun IntRange.toStepCalculator(step: Int, format: Format<Int>?=null) = IntStepCalculator.instance(this,step,format)


class FloatStepCalculator(start: Float, end:Float, step:Int) : AbstractStepCalculator<Float>(start,end, step) {

    override fun hasNext(): Boolean {
        return (start + currentStepIndex * step) <= (end + step)
    }

    override fun getNext(stepIndex: Int, count: Int, currentValue: Float, start: Float, end: Float): Float {
        return start + stepIndex * step
    }

    override fun getCount():Int{
        return ((end - start) / step).toInt() + 1
    }

    companion object{
        fun instance(range: ClosedFloatingPointRange<Float>, step: Int, format: Format<Float>?=null):FloatStepCalculator{
            return FloatStepCalculator(range.start,range.endInclusive,step).apply {
                format?.let {
                    this.format=it
                }
            }
        }
    }
}

fun ClosedFloatingPointRange<Float>.toStepCalculator(step: Int, format: Format<Float>?=null) = FloatStepCalculator.instance(this,step,format)

class DoubleStepCalculator(start: Double, end:Double, step:Int) : AbstractStepCalculator<Double>(start,end, step) {

    override fun hasNext(): Boolean {
        return (start + currentStepIndex * step) <= (end + step)
    }

    override fun getNext(stepIndex: Int, count: Int, currentValue: Double, start: Double, end: Double): Double {
        return start + stepIndex * step
    }

    override fun getCount():Int{
        return ((end - start) / step).toInt() + 1
    }

    companion object{
        fun instance(intRange: ClosedFloatingPointRange<Double>, step: Int, format: Format<Double>?=null):DoubleStepCalculator{
            return DoubleStepCalculator(intRange.start,intRange.endInclusive,step).apply{
                format?.let {
                    this.format=it
                }
            }
        }
    }
}

fun ClosedFloatingPointRange<Double>.toStepCalculator(step: Int, format: Format<Double>?=null) = DoubleStepCalculator.instance(this,step,format)


class DataPeriodStepCalculator(start: LocalDate, end:LocalDate, step:Int,val unit: TemporalUnit = ChronoUnit.DAYS) : AbstractStepCalculator<LocalDate>(start,end, step) {
    val period = start.until(end)

    override fun getNext(
        stepIndex: Int,
        count: Int,
        currentValue: LocalDate,
        start: LocalDate,
        end: LocalDate
    ): LocalDate {
        return start.plus(currentStepIndex*step.toLong(),unit)
    }

    override fun getCount(): Int {
       return (period.get(unit)/step).toInt()
    }

    override fun hasNext(): Boolean {
       return lastValue.plus(step.toLong(),unit) <= end
    }

}

class DataDurationStepCalculator(start: LocalDateTime, end:LocalDateTime, step:Int, val unit: TemporalUnit = ChronoUnit.HOURS) : AbstractStepCalculator<LocalDateTime>(start,end, step) {
    val duration = Duration.between(start,end)

    override fun getNext(
        stepIndex: Int,
        count: Int,
        currentValue: LocalDateTime,
        start: LocalDateTime,
        end: LocalDateTime
    ): LocalDateTime {
        return start.plus(currentStepIndex*step.toLong(),unit)
    }

    override fun getCount(): Int {
        return (duration.get(unit)/step).toInt()
    }

    override fun hasNext(): Boolean {
        return lastValue.plus(step.toLong(),unit) <= end
    }

}